package proyectosdservidor;

import java.awt.List;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Hashtable;

public class ServerCoordinator extends Thread{

    private ArrayList<Server> servidores;     // Lista de Servidores
    private ArrayList<DatagramPacket> paquetes;
    private int cantServers;
    private int port;                    // Puerto del coordinador
    private List consola;
    private ArrayList<List> consolas;
    public static int baldeSize;
    public static double overFlowFactor; // Cuando alcanza este tamaño, se realiza una división.
    private DatagramSocket _socket;
    boolean stop = false;
    private int N = 1;      // Cantidad de servidores disponibles.

    public ServerCoordinator(int cantServers, int initialPort, ArrayList<List> consolas, 
            int baldeSize, double overFlowFactor) throws Exception{
        this.servidores = new ArrayList<Server>();
        this.paquetes = new ArrayList<DatagramPacket>();
        this.baldeSize = baldeSize;
        this.overFlowFactor = overFlowFactor;
        this.cantServers = cantServers;
        this.port = initialPort;
        this.consolas = consolas;
        this.consola = this.consolas.get(0);
        this._socket = new DatagramSocket(port);
    }

    @Override
    public void run(){
        consola.add("Coordinador activo: " + port);
        do{
            try{
                // Se recibe mensaje de dividir a partir de un servidor
                DatagramPacket packet = new DatagramPacket(new byte[255],255);
                _socket.receive(packet);

                // Le damos tiempo para que el servidor que envía el paquete
                // termine su operación para escuchar una respuesta
                sleep(1000);

                paquetes.add(packet);
                ProyectoSDServidorView.obs_servidores.add("El servidor con puerto " + packet.getPort() + " solicita división");
                
                // Procesamiento
                byte[] data = packet.getData();
                String msg = new String(data);
                msg = msg.trim();
                String[] datos = msg.split(";");  // [0] = Se descarta, [1] = cPort
                consola.add("Hacer división: " + packet.getPort());
                int split = this.N - (int)(Math.pow(2,calcularNivel(this.N)));
                this.N++;   // Levantamos otro servidor como activo.
                int servPortADividir = buscarServidor(split);
                int newServPort = servidores.get(N-1).getServerPort();

                // Se actualiza el N de todos los servidores, ya que se va a efectuar una división
                this.incNServs();
                
                // Se envía al remitente del paquete el ID y puerto del servidor a dividir
                // más el puerto del cliente para que le pueda avisar de cambios
                sendMessage("5;" + (N-1) + ":" + newServPort + ";" + datos[1], servPortADividir);

            }catch(Exception ex){
                ex.printStackTrace();
            }
        }while(!stop);
    }


    private int buscarServidor(int idSrv){
        for(Server s: servidores){
            if(s.getServerID() == idSrv){
                return s.getServerPort();
            }
        }
        return 0;
    }

    private int calcularNivel(int N){
        for (int i = 0; i <= 5; i++){
            int p = (int)Math.pow(2, i);
            if(p > N){
                return (i-1);
            }
        }
        return 0;
    }

    private void incNServs(){
        for(Server s: servidores){
            s.incN();
        }
    }

    /**
     * Inicializa el total de (1-10) servidores posibles que queremos correr
     * @param cantServers "Cantidad de servidores a iniciar"
     */
    public void initializeServers() throws Exception{       
        //Inicializamos los servidores
        for (int i = 0; i < cantServers; i++){
            try{
                Server server = new Server(this.port+i+1, consolas.get(i+1), i);  // Se itera a partir de i=1, no sumamos 1
                this.servidores.add(server);
                server.start();
            }catch (Exception ex){
                System.out.print("Error al iniciar servidor"+(i));
                ex.printStackTrace();
            }
        }
    }

    /**
     * Detiene los servidores activos.
     */
    public void detenerServers(){
        for(Server server: servidores)
        {
            server.stopServer();
        }
        for (int i = 0; i < cantServers; i++){
            consolas.get(i).add("Servidor detenido");
        }
    }

    public void detenerServer(int i){
        servidores.get(i).stopServer();
        consolas.get(i).add("Servidor detenido");
    }

    private void sendMessage(String msg, int port) throws Exception{
	_socket.connect(InetAddress.getLocalHost(), port);
        byte[] buffer = msg.getBytes();
        DatagramPacket paquete = new DatagramPacket(buffer, buffer.length);
        _socket.send(paquete);
        _socket.disconnect();
    }
}
